import tensorflow.keras as keras
import numpy as np
import librosa


SAVED_MODEL_PATH = "model.h5"
SAMPLES_TO_CONSIDER = 22050
TEST_AUDIO_FILE_PATH = ["test\\left.wav", 
                        "test\\down.wav"
                        ]


class _Keyword_Spotting_Service:
    
    model = None
    _mappings = [
        "bed",
        "bird",
        "cat",
        "dog",
        "down",
        "eight",
        "five",
        "four",
        "go",
        "happy",
        "house",
        "left",
        "marvin",
        "nine",
        "no",
        "off",
        "on",
        "one",
        "right",
        "seven",
        "sheila",
        "six",
        "stop",
        "three",
        "tree",
        "two",
        "up",
        "wow",
        "yes",
        "zero"
    ]
    _instance = None  # keyword spotting instance (singleton 单例模式)
    
    
    def preprocess(self, file_path, n_mfcc=13, n_fft=2048, hop_length=512):
        
        # load the audio file
        signal, sr = librosa.load(file_path)        
        
        # ensure consistency of the length of the signal
        if len(signal) >= SAMPLES_TO_CONSIDER:
            signal = signal[:SAMPLES_TO_CONSIDER]
        
        # extract MFCCs
        MFCCs = librosa.feature.mfcc(signal, n_mfcc=n_mfcc, n_fft=n_fft, hop_length=hop_length)
        
        return MFCCs.T
    
    
    def predict(self, file_path):
        
        # extract MFCCs
        MFCCs = self.preprocess(file_path)
        
        # convert 2d MFCCs array into 4d array -> (num_samples, segments, coefficients, channels=1)
        MFCCs = MFCCs[np.newaxis, ..., np.newaxis]
        
        # make prediction
        predictions = self.model.predict(MFCCs)  # [ [30 different values] ]
        predicted_index = np.argmax(predictions)
        predicted_keyword = self._mappings[predicted_index]
        
        return predicted_keyword
        
    
def Keyword_Spotting_Service():
    
    if _Keyword_Spotting_Service._instance is None:
        _Keyword_Spotting_Service._instance = _Keyword_Spotting_Service()
        _Keyword_Spotting_Service.model = keras.models.load_model(SAVED_MODEL_PATH)
    
    return _Keyword_Spotting_Service._instance


if __name__ == "__main__":
    
    kss = Keyword_Spotting_Service()
    
    keywords = []
    for f in TEST_AUDIO_FILE_PATH:
        keywords.append(kss.predict(f))
    
    for i in range(len(keywords)):
        print("Predicted keyword {}: {}".format(i, keywords[i]))
    